package cn.zn.modules.pz.his.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.zn.framework.common.utils.Constant;
import cn.zn.framework.common.utils.PageUtils;
import cn.zn.framework.common.vo.FinanceValues;
import cn.zn.framework.redis.RedisUtils;
import cn.zn.modules.loan.old.inter.LoanInter;
import cn.zn.modules.loan.old.inter.vo.LoanDetailVO;
import cn.zn.modules.pz.enums.ProcessTypeEnum;
import cn.zn.modules.pz.his.entity.*;
import cn.zn.modules.pz.his.entity.dto.PzQueryDTO;
import cn.zn.modules.pz.his.entity.vo.*;
import cn.zn.modules.pz.his.repository.PzMainRepository;
import cn.zn.modules.pz.his.service.*;
import cn.zn.modules.pz.inter.ElecSignService;
import cn.zn.modules.pz.inter.vo.ElecSignVO;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PzMainServiceImpl implements PzMainService {
    @Autowired
    private FinanceValues financeValues;
    @Autowired
    private PzMainRepository pzMainRepository;
    @Autowired
    private PzRequestService pzRequestService;
    @Autowired
    private PzExpenseService pzExpenseService;
    @Autowired
    private PzRepayService pzRepayService;
    @Autowired
    private PzAuditService pzAuditService;
    @Autowired
    private PzBudgetService pzBudgetService;
    @Autowired
    private PzMoneyService pzMoneyService;
    @Autowired
    private PzTrafficService pzTrafficService;
    /**
     * 借款接口
     */
    @Autowired
    private LoanInter loanInter;
    /**
     * 签章接口
     */
    @Autowired
    private ElecSignService elecSignService;
    @Autowired
    private RedisUtils redisUtils;

    @Override
    public PageUtils queryPage(PzQueryDTO pzQueryDTO) {
        int page = 0;
        int limit = 10;
        if (pzQueryDTO.getLimit() != null && pzQueryDTO.getLimit() > 0) {
            limit = pzQueryDTO.getLimit().intValue();
        }
        if (pzQueryDTO.getOffset() != null && pzQueryDTO.getOffset() > 0) {
            // 查询page从0开始，此处不+1
            page = pzQueryDTO.getOffset().intValue() / limit;
        }
        // 查询主表
        QPzMain qPzMain = QPzMain.pzMain;
//        Predicate predicate = qPzMain.state.eq(3);
        Predicate predicate = qPzMain.isNotNull().or(qPzMain.isNull());
        if (pzQueryDTO.getAp() != null) {
            predicate = ExpressionUtils.and(predicate, qPzMain.ap.eq(pzQueryDTO.getAp()));
        }
        if (StrUtil.isNotBlank(pzQueryDTO.getApplyType())) {
            predicate = ExpressionUtils.and(predicate, qPzMain.applyType.eq(pzQueryDTO.getApplyType()));
        }
        if (StrUtil.isNotBlank(pzQueryDTO.getApplyCode())) {
            predicate = ExpressionUtils.and(predicate, qPzMain.applyCode.like("%" + pzQueryDTO.getApplyCode().trim().toUpperCase() + "%"));
        }
        if (pzQueryDTO.getMoneyStart() != null && pzQueryDTO.getMoneyEnd() != null) {
            predicate = ExpressionUtils.and(predicate, qPzMain.money.between(pzQueryDTO.getMoneyStart(), pzQueryDTO.getMoneyEnd()));
        } else {
            if (pzQueryDTO.getMoneyStart() != null) {
                predicate = ExpressionUtils.and(predicate, qPzMain.money.goe(pzQueryDTO.getMoneyStart()));
            } else if (pzQueryDTO.getMoneyEnd() != null) {
                predicate = ExpressionUtils.and(predicate, qPzMain.money.loe(pzQueryDTO.getMoneyEnd()));
            }
        }
        if (pzQueryDTO.getApplyDateStart() != null && pzQueryDTO.getApplyDateEnd() != null) {
            predicate = ExpressionUtils.and(predicate, qPzMain.applyDate.between(pzQueryDTO.getApplyDateStart(), pzQueryDTO.getApplyDateEnd()));
        } else {
            if (pzQueryDTO.getApplyDateStart() != null) {
                predicate = ExpressionUtils.and(predicate, qPzMain.applyDate.goe(pzQueryDTO.getApplyDateStart()));
            } else if (pzQueryDTO.getApplyDateEnd() != null) {
                predicate = ExpressionUtils.and(predicate, qPzMain.applyDate.loe(pzQueryDTO.getApplyDateEnd()));
            }
        }
        if (StrUtil.isNotBlank(pzQueryDTO.getKey())) {
            predicate = ExpressionUtils.and(predicate, qPzMain.title.like("%" + pzQueryDTO.getKey().trim() + "%")
                    .or(qPzMain.applyDeptName.like("%" + pzQueryDTO.getKey().trim() + "%"))
                    .or(qPzMain.applyUserName.like("%" + pzQueryDTO.getKey().trim() + "%"))
                    .or(qPzMain.agentUsers.like("%" + pzQueryDTO.getKey().trim() + "%"))
            );
        }
        if (pzQueryDTO.getOnlyMe() != null && pzQueryDTO.getOnlyMe()) {
            predicate = ExpressionUtils.and(predicate, qPzMain.applyUserCode.eq(pzQueryDTO.getApplyUserCode()));
        }
        Page<PzMain> pzMainPage = pzMainRepository.findAll(predicate, PageRequest.of(page, limit, Sort.by(Sort.Order.desc("applyDate"))));
        if (pzMainPage.getTotalElements() == 0) {
            // 返回空
            return new PageUtils(Lists.newArrayList(), Long.valueOf(pzMainPage.getTotalElements()).intValue(), limit, pzMainPage.getNumber());
        }
        // 查询子表内容
        List<PzListVO> pzListVOS = null;
        List<String> mainIds = pzMainPage.getContent().stream().map(o -> o.getMainId()).collect(Collectors.toList());
        List<String> applyCodes = pzMainPage.getContent().stream().map(o -> o.getApplyCode()).collect(Collectors.toList());
        if (pzQueryDTO.getApplyType().equals("spending")) {
            Map<String, PzRequest> pzRequestMap = pzRequestService.findByMainIds(pzQueryDTO.getAp(), mainIds).stream().collect(Collectors.toMap(item -> item.getMainId(), item -> item, (o1, o2) -> o1));
            pzListVOS = pzMainPage.getContent().stream().map(o -> {
                PzListVO pzListVO = new PzListVO();
                BeanUtils.copyProperties(o, pzListVO);
                PzRequest pzRequest = pzRequestMap.get(o.getMainId());
                if (pzRequest != null) {
                    pzListVO.setIsExpense(pzRequest.getIsExpense());
                    pzListVO.setReason(pzRequest.getReason());
                }
                return pzListVO;
            }).collect(Collectors.toList());
        } else if (pzQueryDTO.getApplyType().equals("expense")) {
            Map<String, PzExpense> pzExpenseMap = pzExpenseService.findByMainIds(pzQueryDTO.getAp(), mainIds).stream().collect(Collectors.toMap(item -> item.getMainId(), item -> item, (o1, o2) -> o1));
            Map<String, LoanDetailVO> loanMap = loanInter != null ? loanInter.findByApplyCodes(applyCodes).stream().collect(Collectors.toMap(LoanDetailVO::getApplyCode, o -> o)) : null;
            pzListVOS = pzMainPage.getContent().stream().map(o -> {
                PzListVO pzListVO = new PzListVO();
                BeanUtils.copyProperties(o, pzListVO);
                PzExpense pzExpense = pzExpenseMap.get(o.getMainId());
                if (pzExpense != null) {
                    pzListVO.setIsExistApply(pzExpense.getIsExistApply());
                    pzListVO.setReason(pzExpense.getReason());
                    pzListVO.setIsAppoint(pzExpense.getIsAppoint());
                }
                if (loanMap != null) {
                    LoanDetailVO loanDetailVO = loanMap.get(o.getApplyCode());
                    pzListVO.setIsExistLoan(loanDetailVO != null);
                }
                return pzListVO;
            }).collect(Collectors.toList());
        } else if (pzQueryDTO.getApplyType().equals("repay")) {
            Map<String, PzRepay> pzRepayMap = pzRepayService.findByMainIds(pzQueryDTO.getAp(), mainIds).stream().collect(Collectors.toMap(item -> item.getMainId(), item -> item, (o1, o2) -> o1));
            pzListVOS = pzMainPage.getContent().stream().map(o -> {
                PzListVO pzListVO = new PzListVO();
                BeanUtils.copyProperties(o, pzListVO);
                PzRepay pzRepay = pzRepayMap.get(o.getMainId());
                if (pzRepay != null) {
                    pzListVO.setReason(pzRepay.getReason());
                }
                return pzListVO;
            }).collect(Collectors.toList());
        }
        return new PageUtils(pzListVOS, Long.valueOf(pzMainPage.getTotalElements()).intValue(), limit, pzMainPage.getNumber());
    }

    @Override
    public PzTicketVO findTicket(Integer ap, String mainId) {
        QPzMain qpzMain = QPzMain.pzMain;
        Optional<PzMain> pzMainOpt = pzMainRepository.findOne(qpzMain.ap.eq(ap).and(qpzMain.mainId.eq(mainId)));
        PzTicketVO pzTicket = null;
        if (pzMainOpt.isPresent()) {
            PzMain pzMain = pzMainOpt.get();
            ProcessTypeEnum processTypeEnum = ProcessTypeEnum.findByDeployKey(pzMain.getApplyType());
            switch (processTypeEnum) {
                case SPENDING:
                    pzTicket = this.buildSpending(pzMain, ap, mainId);
                    break;
                case EXPENSE:
                    pzTicket = this.buildExpense(pzMain, ap, mainId);
                    break;
                case REPAY:
                    pzTicket = this.buildRepay(pzMain, ap, mainId);
                    break;
            }
            // 查询审批
            Map<String, String> auditMap = Maps.newHashMap();
            // 查询电子签章
            Map<String, ElecSignVO> signMap = Maps.newHashMap();
            if (elecSignService != null) {
                Map<String, ElecSignVO> querySignMap = elecSignService.findSigns(pzMain.getApplyCode()).stream().collect(Collectors.toMap(ElecSignVO::getUserCode, item -> item, (o1, o2) -> o1));
                signMap.putAll(querySignMap);
            }
            // 查询审批
            pzAuditService.findByMainId(ap, mainId).forEach(pzAudit -> {
                String imgData = StrUtil.isNotBlank(pzAudit.getSignImgPath()) ? financeValues.getFile().getDomain() + pzAudit.getSignImgPath() : "";
                if (!signMap.isEmpty() && signMap.containsKey(pzAudit.getAssigneeCode())) {
                    ElecSignVO elecSignVO = signMap.get(pzAudit.getAssigneeCode());
                    switch (elecSignVO.getElecSignType()) {
                        case URL:
                            imgData = elecSignVO.getContent();
                            break;
                        case BASE64:
                            imgData = "data:image/jpeg;base64," + elecSignVO.getContent();
                            break;
                    }
                }
                auditMap.put(pzAudit.getGroupCode(), StrUtil.isNotBlank(pzAudit.getSignImgPath()) ? "" : pzAudit.getAssigneeName());
                auditMap.put(pzAudit.getGroupCode().concat("Img"), imgData);
                auditMap.put(pzAudit.getGroupCode().concat("ConfirmImg"), StrUtil.isNotBlank(pzAudit.getConfirmImgPath()) ? financeValues.getFile().getDomain() + pzAudit.getConfirmImgPath() : "");
                auditMap.put(pzAudit.getGroupCode().concat("Time"), pzAudit.getEndTime() != null ? pzAudit.getEndTime().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")) : "");
            });
            pzTicket.setAuditMap(auditMap);
            pzTicket.setQrCodeUrl("/common/qrCode?content=" + pzMain.getApplyCode());
            pzTicket.setSchoolName(financeValues.getCommon().getSysName());
        }
        return pzTicket;
    }

    @Override
    public IPz findDetail(Integer fiscalYear, String applyType, String mainId) {
        IPz pz = null;
        if (applyType.equals("spending")) {
            PzRequest pzRequest = pzRequestService.findByMainId(fiscalYear, mainId);
            pz = new PzRequestVO();
            BeanUtils.copyProperties(pzRequest, pz);
        } else if (applyType.equals("expense")) {
            PzExpense pzExpense = pzExpenseService.findByMainId(fiscalYear, mainId);
            pz = new PzExpenseVO();
            BeanUtils.copyProperties(pzExpense, pz);
        } else if (applyType.equals("repay")) {
            PzRepay pzRepay = pzRepayService.findByMainId(fiscalYear, mainId);
            pz = new PzRepayVO();
            BeanUtils.copyProperties(pzRepay, pz);
        }
        QPzMain qPzMain = QPzMain.pzMain;
        Optional<PzMain> pzMainOpt = pzMainRepository.findOne(qPzMain.ap.eq(fiscalYear).and(qPzMain.mainId.eq(mainId)));
        if (pzMainOpt.isPresent()) {
            PzMainVO pzMainVO = new PzMainVO();
            BeanUtils.copyProperties(pzMainOpt.get(), pzMainVO);
            pz.setPzMainVO(pzMainVO);
        }
        return pz;
    }

    @Override
    public boolean isCurPeriod(Integer year) {
        return redisUtils.hget(Constant.REDIS_FINANCIAL, "year", Integer.class).equals(year);
    }

    private PzTicketVO buildRepay(PzMain pzMain, Integer ap, String mainId) {
        PzMainVO pzMainVO = new PzMainVO();
        BeanUtils.copyProperties(pzMain, pzMainVO);
        PzRepayVO pzRepayVO = new PzRepayVO();
        PzRepay pzRepay = pzRepayService.findByMainId(ap, mainId);
        if (pzRepay != null) {
            BeanUtils.copyProperties(pzRepay, pzRepayVO);
            BeanUtils.copyProperties(pzRepay, pzMainVO);
            pzRepayVO.setPzMainVO(pzMainVO);
        }
        // 基本信息
        String templateName;
        if (pzMain.getSpendingType() == 4 || pzMain.getSpendingType() == 5) {
            templateName = "myPrint2_2";
        } else {
            templateName = "myPrint2_1";
        }
        // 当预算明细为不同部门时，预算部门显示为“采购小组”
        List<PzBudget> pzBudgets = pzBudgetService.findByMainId(ap, mainId);
        if (pzBudgets != null) {
            String deptCode = "";
            for (PzBudget pzBudget : pzBudgets) {
                if (!StrUtil.isNotBlank(deptCode)) {
                    deptCode = pzBudget.getDeptName();
                } else {
                    if (!deptCode.equals(pzBudget.getDeptName())) {
                        pzRepayVO.getPzMainVO().setApplyDeptName("采购小组");
                        break;
                    }
                }
            }
        }
        //pzRepayVO.setMoneyStr(this.buildMoneyStr(pzRepay.getMoneyTypeIscash(), ap, mainId));
        pzRepayVO.setPrintTime(Calendar.getInstance().getTime());
        return PzTicketVO.builder()
                .templateName(templateName)
                .pz(pzRepayVO)
                .build();
    }

    private PzTicketVO buildExpense(PzMain pzMain, Integer ap, String mainId) {
        PzMainVO pzMainVO = new PzMainVO();
        BeanUtils.copyProperties(pzMain, pzMainVO);
        PzExpenseVO pzExpenseVO = new PzExpenseVO();
        PzExpense pzExpense = pzExpenseService.findByMainId(ap, mainId);
        if (pzExpense != null) {
            BeanUtils.copyProperties(pzExpense, pzExpenseVO);
            BeanUtils.copyProperties(pzExpense, pzMainVO);
            pzExpenseVO.setPzMainVO(pzMainVO);
        }
        // 基本信息
        String templateName;
        if (pzMain.getSpendingType() == 4 || pzMain.getSpendingType() == 5) {
            templateName = "myPrint2_2";
        } else {
            templateName = "myPrint2_1";
            // 查询是否存在借款
            if (loanInter != null) {
                Double loanMoney = loanInter.findLoan(pzMain.getApplyCode());
                if (loanMoney != null && Double.compare(loanMoney, 0D) > 0) {
                    pzExpenseVO.setLoanMoney(loanMoney);
                    templateName = "myPrint2_3";
                }
            }
        }
        // 当预算明细为不同部门时，预算部门显示为“采购小组”
        List<PzBudget> pzBudgets = pzBudgetService.findByMainId(ap, mainId);
        if (pzBudgets != null) {
            String deptCode = "";
            for (PzBudget pzBudget : pzBudgets) {
                if (!StrUtil.isNotBlank(deptCode)) {
                    deptCode = pzBudget.getDeptName();
                } else {
                    if (!deptCode.equals(pzBudget.getDeptName())) {
                        pzExpenseVO.setApplyDeptName("采购小组");
                        break;
                    }
                }
            }
        }
        // 查询经费
        pzExpenseVO.setMoneyStr(this.buildMoneyStr(pzExpense.getMoneyTypeIscash(), ap, mainId));
        pzExpenseVO.setPrintTime(Calendar.getInstance().getTime());
        return PzTicketVO.builder()
                .templateName(templateName)
                .pz(pzExpenseVO)
                .build();
    }

    private PzTicketVO buildSpending(PzMain pzMain, Integer ap, String mainId) {
        PzMainVO pzMainVO = new PzMainVO();
        BeanUtils.copyProperties(pzMain, pzMainVO);
        PzRequestVO pzRequestVO = new PzRequestVO();
        PzRequest pzRequest = pzRequestService.findByMainId(ap, mainId);
        if (pzRequest != null) {
            BeanUtils.copyProperties(pzRequest, pzRequestVO);
            BeanUtils.copyProperties(pzRequest, pzMainVO);
        }
        pzRequestVO.setPzMainVO(pzMainVO);
        // 查询出差
        if (pzMain.getSpendingType() == 2) {
            String reason = pzRequest.getReason();
            Set<String> expenseUnits = Sets.newHashSet();
            Set<LocalDate> tripTimes = Sets.newHashSet();
            Set<String> traffics = Sets.newHashSet();
            Set<String> places = Sets.newHashSet();
            StringBuffer reasonBuffer = new StringBuffer();
            reasonBuffer.append(reason).append("<br/>");
            pzTrafficService.findByMainId(ap, mainId).stream().limit(4).forEach(pzTraffic -> {
                reasonBuffer.append(pzTraffic.toString()).append("<br/>");
                if (StrUtil.isNotBlank(pzTraffic.getExpenseUnit())) {
                    expenseUnits.add(pzTraffic.getExpenseUnit());
                }
                if (pzTraffic.getStartTime() != null) {
                    tripTimes.add(pzTraffic.getStartTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
                }
                if (pzTraffic.getEndTime() != null) {
                    tripTimes.add(pzTraffic.getEndTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
                }
                if (StrUtil.isNotBlank(pzTraffic.getTraffic())) {
                    traffics.add(pzTraffic.getTraffic());
                }
                if (StrUtil.isNotBlank(pzTraffic.getStartPlace()) && StrUtil.isNotBlank(pzTraffic.getEndPlace())) {
                    places.add(pzTraffic.getStartPlace() + " 至 " + pzTraffic.getEndPlace());
                }
            });
            String[] placeArr = places.toArray(new String[0]);
            pzRequestVO.setPzTripTicketVO(PzTripTicketVO.builder()
                    .reasonTrip(reason)
                    .expenseUnit(expenseUnits.size() > 0 ? StrUtil.join(",", expenseUnits) : "")
                    .traffic(traffics.size() > 0 ? StrUtil.join(",", traffics) : "")
                    .tripTime(tripTimes.size() > 0 ? "自 " + Collections.min(tripTimes).format(DateTimeFormatter.ofPattern("MM月dd日")) + " 至 " + Collections.max(tripTimes).format(DateTimeFormatter.ofPattern("MM月dd日")) : "")
                    .place1(places.size() > 0 ? placeArr[0] : "")
                    .place2(places.size() > 1 ? placeArr[1] : "")
                    .place3(places.size() > 2 ? placeArr[2] : "")
                    .place4(places.size() > 3 ? placeArr[3] : "")
                    .build());
        }
        // 查询经费
        StringBuilder budgetNameBuilder = new StringBuilder("");
        List<String> budgetNames = pzBudgetService.findByMainId(ap, mainId).stream().limit(2).map(pzBudget -> pzBudget.getProjectName()).collect(Collectors.toList());
        for (int i = 0; i < budgetNames.size(); i++) {
            if (StrUtil.isNotBlank(budgetNames.get(i))) {
                budgetNameBuilder.append(budgetNames.get(i));
            }
            if (budgetNames.size() > 2 && i + 1 == 2) {
                budgetNameBuilder.append("，等");
            } else {
                budgetNameBuilder.append("<br/>");
            }
        }
        pzRequestVO.setBudgetStr(budgetNameBuilder.toString());

        // 单据基本信息
        String templateName = "";
        if (pzMain.getMoney() <= 1000) {
            templateName = "myPrint1_1";
        } else if (pzMain.getMoney() > 1000 && pzMain.getMoney() <= 30000) {
            templateName = "myPrint1_3";
        } else if (pzMain.getMoney() > 30000 && pzMain.getMoney() <= 50000) {
            templateName = "myPrint1_4";
        } else if (pzMain.getMoney() > 50000) {
            templateName = "myPrint1_5";
        }
        return PzTicketVO.builder()
                .templateName(templateName)
                .pz(pzRequestVO)
                .build();
    }

    private String buildMoneyStr(Boolean isCash, Integer ap, String mainId) {
        StringBuffer moneyNameBuffer = new StringBuffer();
        if (isCash) {
            moneyNameBuffer.append("现金");
        } else {
            List<PzMoneyVO> pzMonies = pzMoneyService.findByMainId(ap, mainId);
            List<String> moneyNames = pzMonies.stream().limit(2).map(o -> o.getMoneyTypeName().concat("-").concat(o.getProjectName())).collect(Collectors.toList());
            for (int i = 0; i < moneyNames.size(); i++) {
                moneyNameBuffer.append(moneyNames.get(i));
                if (i < moneyNames.size() - 1) {
                    moneyNameBuffer.append("<br/>");
                }
            }
            if (pzMonies.size() > 2) {
                moneyNameBuffer.append("，等");
            }
        }
        return moneyNameBuffer.toString();
    }

}
